/* eslint-disable no-use-before-define */
/* eslint-disable camelcase */
import {
    Component,
    OnInit,
    Input,
    Output,
    EventEmitter,
    ChangeDetectorRef,
    SimpleChanges,
    ViewChild,
    ElementRef,
    Renderer2,
    Inject,
    AfterViewInit,
    OnChanges,
    OnDestroy,
    forwardRef
} from '@angular/core';
import { Color, ColorString } from './helpers/color.class';
import { ColorPickerControl, ColorType } from './helpers/control.class';
import { getValueByType } from './helpers/helper.functions';
import { DOCUMENT } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

const FARRIS_ColorPicker_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => ColorPickerComponent),
    multi: true
};

@Component({
    selector: 'farrisui-color-picker, color-picker',
    templateUrl: './color-picker.component.html',
    styleUrls: [`./color-picker.component.scss`],
    providers: [FARRIS_ColorPicker_VALUE_ACCESSOR]
})
export class ColorPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {
    @Input() color: string;

    @Input() disabled = false;

    @Input() size: string;

    @Input()
    set showAlpha(v: boolean) {
        this._showAlpha = !v;
    }

    get showAlpha() {
        if (this.colorFormat && this.colorFormat.indexOf('a') > 0) {
            return true;
        }
        if (this.control.initType && this.control.initType.indexOf('a') > 0) {
            return true;
        }
        return this._showAlpha;
    }

    @Input()
    set colorFormat(v: ColorType) {
        this._colorFormat = v;
    }

    get colorFormat() {
        if (!this._colorFormat) {
            if (this.control.initType) {
                return this.control.initType;
            }
        }
        return this._colorFormat;
    }

    @Input() presets: Array<string> = [];

    /**
     * @deprecated
     */
    @Input() selectOnly: boolean;

    @Input() editable = false;

    @Input()
    control: ColorPickerControl;

    @Output()
    public change: EventEmitter<ColorString> = new EventEmitter(false);

    @Output()
    public activeChange: EventEmitter<ColorString> = new EventEmitter(false);

    @ViewChild('trigger')
    public trigger: ElementRef;

    @ViewChild('pickerPanel')
    public pickerPanel: ElementRef;

    public isShowPanel = false;

    private _showAlpha = false;

    private _colorFormat: ColorType;

    private _preColor: string;

    set preColor(v: string) {
        this._color = v;
        this._preColor = v;

        this.renderer.setStyle(this.trigger.nativeElement, 'background', this._preColor);
    }

    get preColor() {
        return this._preColor;
    }

    set _color(v: string) {
        if (this.control) {
            this.control.setValueFrom(v);
            this.findColorType(this.colorFormat);
        }
    }

    get _color() {
        if (this.colorFormat) {
            return getValueByType(new Color(this.color), ColorType[this.colorFormat]);
        }
        return this.color;
    }

    onModelChange = (val) => {};

    onTouched = (val) => {};

    // 只支持两种 类型变化
    presetColorType = [
        {
            text: 'HEX',
            type: ColorType.hex
        },
        {
            text: 'RGBA',
            type: ColorType.rgba
        }
    ];

    currentColorTypeIndex = -1;

    constructor(
        private readonly cdr: ChangeDetectorRef,
        private renderer: Renderer2,
        protected readonly elementRef: ElementRef,
        @Inject(DOCUMENT) document
    ) {
        document.addEventListener('click', (e) => {
            this.isShowPanel = false;
        });
    }

    ngOnInit(): void {
        if (!this.control) {
            this.control = new ColorPickerControl();
        }

        if (this.color) {
            this.control.setValueFrom(this.color);
        }
        if (!this.control.hasPresets()) {
            this.control.setColorPresets(this.presets);
        }
        this.control.valueChanges.subscribe((value) => {
            this.cdr.markForCheck();
            this.color = getValueByType(value, ColorType[this.colorFormat]);
            this.activeChange.emit(getValueByType(value, ColorType[this.colorFormat]));
        });
        this.preColor = getValueByType(new Color(this.color), ColorType[this.colorFormat]);
    }

    ngAfterViewInit(): void {
        document.body.insertBefore(this.pickerPanel.nativeElement, null);
    }

    public ngOnDestroy(): void {
        this.control.unsubscribe();
        this.cdr.detach();
    }

    public ngOnChanges(changes: SimpleChanges): void {
        if (this.color && this.control && getValueByType(this.control.value, this.control.initType) !== this.color) {
            this.control.setValueFrom(this.color);
        }
        if (changes.colorFormat && !changes.colorFormat.isFirstChange()) {
            this.preColor = getValueByType(new Color(this.color), this.colorFormat);
            this.onModelChange(this.preColor);
        }
    }

    onBlur($event) {
        this.onModelChange(this.preColor);
    }

    public clear(): void {
        this.color = '';
        this.renderer.setStyle(this.trigger.nativeElement, 'background', '');
    }

    public commit(): void {
        this.renderer.setStyle(this.trigger.nativeElement, 'background', this.color);
        this.preColor = this.color;
        this.onModelChange(this.preColor);
        this.isShowPanel = false;
    }

    public toggleShowPanel(e): void {
        e.stopPropagation();
        if (this.disabled) {
            return;
        }
        this.isShowPanel = !this.isShowPanel;
        const { top: elTop, left: elLeft, height: elHeight } = this.elementRef.nativeElement.getBoundingClientRect();
        const { height: panelHeight, width: panelWidth } = this.pickerPanel.nativeElement.getBoundingClientRect();
        if (elTop + panelHeight + 30 > window.innerHeight) {
            this.renderer.setStyle(this.pickerPanel.nativeElement, 'top', `${elTop - panelHeight - 5}px`);
        } else {
            this.renderer.setStyle(this.pickerPanel.nativeElement, 'top', `${elTop + elHeight + 5}px`);
        }
        this.renderer.setStyle(this.pickerPanel.nativeElement, 'left', `${elLeft}px`);
    }

    writeValue(val: any): void {
        if (val === undefined || val === null) {
            val = '';
        }
        this.color = val;
        if (this._colorFormat) {
            this.preColor = getValueByType(new Color(this.color), ColorType[this.colorFormat]);
        } else {
            this.preColor = val;
        }
    }

    registerOnChange(fn: any): void {
        this.onModelChange = fn;
    }

    registerOnTouched(fn: any): void {
        this.onTouched = fn;
    }

    setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
    }

    private findColorType(typeStr) {
        const findTypeIndex = this.presetColorType.findIndex((item) => item.type === typeStr);
        this.currentColorTypeIndex = findTypeIndex;
    }

    changeColorFormatByIcon(direction = 'up') {
        const total = this.presetColorType.length;
        this.currentColorTypeIndex = (total + this.currentColorTypeIndex + (direction === 'up' ? 1 : -1)) % total;
        this.colorFormat = this.presetColorType[this.currentColorTypeIndex].type;
    }
}
